﻿using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using System.IO;

namespace CampaignDataPdfConverter
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            try
            {
                Run(args);
            }
            catch (Exception e)
            {
                string message = "Conversion failed: " + e.Message;
                if (message.Contains("cannot access"))
                    message = message + " Most likely this file is already open in Excel. If so, please close it and try again.";
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        static void Run(string[] args)
        {
            // check that the input file exists
            string inputFileName = null;
            if (args.Length == 1)
                inputFileName = args[1];
            else
            {
                OpenFileDialog dlgOpenFile = new OpenFileDialog();
                dlgOpenFile.Title = "Please select the FDA DDMAC submission file (in PDF)";
                dlgOpenFile.InitialDirectory = "c:\\";
                dlgOpenFile.Filter = "PDF files (*.pdf)|*.pdf";
                dlgOpenFile.FilterIndex = 2;
                dlgOpenFile.RestoreDirectory = true;

                if (!(dlgOpenFile.ShowDialog() == DialogResult.OK))
                    return;
                inputFileName = dlgOpenFile.FileName;
                try
                {
                    // see if it can actually be open for reading
                    Stream s = dlgOpenFile.OpenFile();
                    if (s == null)
                        throw new Exception("No file chosen");
                    s.Close();
                    s.Dispose();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
            if (!File.Exists(inputFileName))
            {
                Console.Error.WriteLine("ERROR: file " + inputFileName + " does not exist");
                Environment.Exit(1);
            }
            if (!inputFileName.EndsWith(".pdf"))
            {
                Console.Error.WriteLine("ERROR: file " + inputFileName + " is not a PDF file");
                Environment.Exit(1);
            }            

            // convert it into text
            string fullName = new FileInfo(inputFileName).FullName;
            Process p = Process.Start("pdftotext.exe", "-layout \"" + fullName + "\"");
            while (!p.HasExited)
                p.WaitForExit(1000);
            int exitCode = p.ExitCode;
            if (exitCode != 0)
                throw new Exception("PDF conversion to text failed");
            inputFileName = Regex.Replace(inputFileName, ".pdf\\Z", ".txt");
            if (!File.Exists(inputFileName))
                throw new Exception("the converted text file not found");

            // read the text and create all the ad groups
            CampaignReader reader = new CampaignReader();
            using (StreamReader r = new StreamReader(inputFileName))
            {
                string line;
                int lineNumber = 0;
                while (null != (line = r.ReadLine()))
                {
                    ++lineNumber;
                    reader.ConsumeLine(line, lineNumber);
                }
            }
            // ...and complain about how many lines were skipped
            if (reader.skipped_.Count != 0)
                Console.Error.WriteLine("WARNING: " + reader.skipped_.Count + " lines were skipped (see skipped.txt for skipped lines)");
            string skippedLinesFile = fullName + ".skipped.lines.txt";
            using (StreamWriter w = new StreamWriter(skippedLinesFile))
            {
                w.WriteLine("SKIPPED LINES. IF THEY HAVE SOMETHING IMPORTANT -- IT WAS NOT INCLUDED. CHECK THEM MANUALLY!");
                w.WriteLine("============================================================================================");
                foreach (var skippedLine in reader.skipped_)
                    w.WriteLine(skippedLine.Key);
            }
            Process.Start("explorer.exe", skippedLinesFile);

            // 1. write everything in XML format so that it's easier for any other software you may have (or borrow)
            Brands brands = reader.brands_;
            string campaignsXmlFileName = fullName + ".Campaigns.xml";
            Console.WriteLine("Creating an XML file with all campaign information read (" + campaignsXmlFileName + ")...");
            using (StreamWriter w = new StreamWriter(campaignsXmlFileName))
            {
                var xmlConverter = new System.Xml.Serialization.XmlSerializer(typeof(Brand));
                foreach (var brand in brands.brands)
                {
                    try
                    {
                        xmlConverter.Serialize(w, brand);
                    }
                    catch (Exception e)
                    {
                        Console.Error.WriteLine("Error converting into XML: " + e);
                    }
                }
            }

            // 2. write the ads in Excel format
            string adTemplateFileName = fullName + ".AdTemplate.xls";
            Console.WriteLine("Creating " + adTemplateFileName + "...");
            using (StreamWriter w = new StreamWriter(adTemplateFileName))
            {
                WriteAdGroupTabTxt(brands, w, true /* write ad variations */);
            }
            Process.Start("explorer.exe", adTemplateFileName);

            // 3. write the ads in Excel format (but with no variations)
            string adTemplateNoVariationsFileName = fullName + ".AdTemplate-NO_VARIATIONS.xls";
            Console.WriteLine("Creating " + adTemplateNoVariationsFileName + "...");
            using (StreamWriter w = new StreamWriter(adTemplateNoVariationsFileName))
            {
                WriteAdGroupTabTxt(brands, w, false /* don't write ad variations */);
            }
            Process.Start("explorer.exe", adTemplateNoVariationsFileName);

            // 4. Writing the keywords in Excel format
            string keywordTemplateFileName = fullName + ".KeywordTemplate.xls";
            Console.WriteLine("Creating " + keywordTemplateFileName + "...");
            using (StreamWriter w = new StreamWriter(keywordTemplateFileName))
            {
                int keywordNumber = 0;
                foreach (var brand in reader.brands_.brands)
                {
                    foreach (var campaign in brand.campaigns)
                    {
                        foreach (var adGroup in campaign.adGroups)
                        {
                            foreach (string keyword in adGroup.keywords)
                            {
                                if (0 == keywordNumber % 96)
                                    WriteKeywordHeaderTxt(w);
                                keywordNumber = keywordNumber + 1;
                                WriteKeywordTxt(w, keywordNumber, adGroup, keyword);
                            }
                        }
                    }
                }
            }
            Process.Start("explorer.exe", keywordTemplateFileName);

            // oh, forgot about an XML file
            Process.Start("explorer.exe", campaignsXmlFileName);
        }

        #region Writing the Tab Text

        private static void WriteAdGroupTabTxt(Brands brands, StreamWriter w, bool writeAdVariations)
        {
            int lineNo = 1;
            foreach (var brand in brands.brands)
            {
                if (String.IsNullOrEmpty(brand.brandName))
                    continue;
                foreach (var campaign in brand.campaigns)
                {
                    if (String.IsNullOrEmpty(campaign.campaignType))
                        continue;
                    foreach (var adGroup in campaign.adGroups)
                    {
                        if (String.IsNullOrEmpty(adGroup.adGroupName))
                            continue;
                        WriteAdGroupHeaderTxt(w, ref lineNo, brand, campaign, adGroup);
                        for (int adIndex = 0; adIndex != adGroup.ads.Count; ++adIndex)
                        {
                            Ad ad = adGroup.ads[adIndex];
                            int adNumber = adIndex + 1; // so that Ad Number = 1 when Ad Index = 0, etc.
                            var adLines = WriteAdContentTxt(w, ref lineNo, ad, adNumber);
                            if (writeAdVariations)
                                WriteAdVariations(w, ref lineNo, ad, adLines);
                        }
                    }
                }
            }
        }

        private static void WriteKeywordTxt(StreamWriter w, int keywordNumber, AdGroup adGroup, string keyword)
        {
            w.WriteLine("\t" + keywordNumber + "\t" + keyword + "\t" + adGroup.desktopUrl + "\t" + adGroup.mobileUrl);
        }

        private static void WriteKeywordHeaderTxt(StreamWriter w)
        {
            w.WriteLine();
            w.WriteLine("\tKeywords");
            w.WriteLine("\t\t\tDestination URLs");
            w.WriteLine("\t#\tKeyword\tDesktop/Tablet\tMobile");
            w.WriteLine();
            w.WriteLine("\t*Misspelled words shown below are intentionally mispelled");
        }

        public class AdLineNumbers
        {
            public int
                titleLineNo_ = -1,
                line1LineNo_ = -1,
                line2LineNo_ = -1,
                isiLineNo_ = -1,
                displayUrl1LineNo_ = -1,
                shortDisplayUrl1LineNo_ = -1,
                displayUrl2LineNo_ = -1,
                shortDisplayUrl2LineNo_ = -1
                ;
        }

        private static AdLineNumbers WriteAdContentTxt(StreamWriter w, ref int lineNo, Ad ad, int adNumber)
        {
            AdLineNumbers result = new AdLineNumbers();
            w.WriteLine("\tAd " + adNumber); ++lineNo;
            w.WriteLine(); ++lineNo;
            w.WriteLine("\tTraditional Format"); ++lineNo;
            result.titleLineNo_ = lineNo; w.WriteLine("\tTitle\t\t" + ad.headline); ++lineNo;
            result.line1LineNo_ = lineNo; w.WriteLine("\tLine 1\t\t" + ad.line1); ++lineNo;
            result.line2LineNo_ = lineNo; w.WriteLine("\tLine 2\t\t" + ad.line2); ++lineNo;
            result.isiLineNo_ = lineNo; w.WriteLine("\tISI\t\tClick to see full safety and prescribing information, including boxed warning. More info"); ++lineNo;
            result.displayUrl1LineNo_ = lineNo; w.WriteLine("\tDisplay URL 1\t\t" + ad.type1Url); ++lineNo;
            result.shortDisplayUrl1LineNo_ = lineNo; w.WriteLine("\tShortened Display URL 1\t\t" + ad.shortededType1Url); ++lineNo;
            result.displayUrl2LineNo_ = lineNo; w.WriteLine("\tDisplay URL 2\t\t" + ad.type2Url); ++lineNo;
            result.shortDisplayUrl2LineNo_ = lineNo; w.WriteLine("\tShortened Display URL 2\t\t" + ad.shortededType2Url); ++lineNo;
            w.WriteLine(); ++lineNo;
            return result;
        }

        private static void WriteAdGroupHeaderTxt(StreamWriter w, ref int lineNo, Brand brand, Campaign campaign, AdGroup adGroup)
        {
            w.WriteLine(); ++lineNo;
            w.WriteLine("\tBrand:\t\t" + brand.brandName); ++lineNo;
            w.WriteLine("\tCampaign Type:\t\t" + campaign.campaignType); ++lineNo;
            w.WriteLine("\tProject Number:\t\t" + campaign.projectNumber); ++lineNo;
            w.WriteLine("\tAd & Keyword Group:\t\t" + adGroup.adGroupName); ++lineNo;
        }

        private static void WriteAdVariations(StreamWriter w, ref int lineNo, Ad ad, AdLineNumbers lineNumbers)
        {
            string titleCell = "D" + lineNumbers.titleLineNo_;
            string line1Cell = "D" + lineNumbers.line1LineNo_;
            string line2Cell = "D" + lineNumbers.line2LineNo_;
            string isiCell = "D" + lineNumbers.isiLineNo_;
            string displayUrl1Cell = "D" + lineNumbers.displayUrl1LineNo_;
            string shortenedDisplayUrl1Cell = "D" + lineNumbers.shortDisplayUrl1LineNo_;
            string displayUrl2Cell = "D" + lineNumbers.displayUrl2LineNo_;
            string shortenedDisplayUrl2Cell = "D" + lineNumbers.shortDisplayUrl2LineNo_;
            string adVariationsFormulas = adVariationsFormulaTemplate_
                .Replace("$titleCell", titleCell)
                .Replace("$line1Cell", line1Cell)
                .Replace("$line2Cell", line2Cell)
                .Replace("$isiCell", isiCell)
                .Replace("$displayUrl1Cell", displayUrl1Cell)
                .Replace("$shortenedDisplayUrl1Cell", shortenedDisplayUrl1Cell)
                .Replace("$displayUrl2Cell", displayUrl2Cell)
                .Replace("$shortenedDisplayUrl2Cell", shortenedDisplayUrl2Cell);
            lineNo = lineNo + CountNumberOfLines(adVariationsFormulas);
            w.WriteLine(adVariationsFormulas); 
            ++lineNo; // we just introduced another line ending by calling WriteLine(...) above
        }

        private static int CountNumberOfLines(string adVariationsFormulas)
        {
            int result = 0;
            int index = 0;
            while (index < adVariationsFormulas.Length && 
                -1 != (index = adVariationsFormulas.IndexOf('\n', index)))
            {
                ++result;
                ++index;
            }
            return result;
        }

        // you can get a similar string to the one below by pressing Ctrl+` in Excel
		static readonly string adVariationsFormulaTemplate_ = @"	
	Format 1		Format 2
	=D8		=D8
	=D12		=D12
	=D14		=D15
	=D9		=CONCATENATE(D9,"" "",D10)
	=D10		Click to see full safety and prescribing information, including boxed warning. More info
	Click to see full safety and prescribing information, including boxed warning. More info		
			
	Format 3		Format 4
	=D8		=CONCATENATE(D8,"" | "",D13)
	=CONCATENATE(D9,"" "",D10)		=D12
	=D12		=D14
	=D14		=D9
	Click to see full safety and prescribing information, including boxed warning. More info		=D10
			Click to see full safety and prescribing information, including boxed warning. More info
			
	Format 5		Format 6
	=CONCATENATE(D8,"" | "",D15)		=CONCATENATE(D8,"" | "",D12)
	=D12		=D9
	=D14		=D10
	=D9		=D12
	=D10		=D14
	Click to see full safety and prescribing information, including boxed warning. More info		Click to see full safety and prescribing information, including boxed warning. More info
			
	Format 7		Format 8
	=CONCATENATE(D9,"" | "",D14)		=CONCATENATE(D8,"" - "",D9)
	=D9		=D12
	=D10		=D14
	=D12		=D10
	=D14		Click to see full safety and prescribing information, including boxed warning. More info
	Click to see full safety and prescribing information, including boxed warning. More info		
			
	Format 9		Format 10
	=CONCATENATE(D8,"" - "",D9)		=CONCATENATE(D8,"" - "",D9,"" | "",D12)
	=D10		=D12
	=D12		=D14
	=D14		=D10
	Click to see full safety and prescribing information, including boxed warning. More info		Click to see full safety and prescribing information, including boxed warning. More info
			
	Format 11		Format 12
	=CONCATENATE(D8,"" - "",D9,"" | "",D15)		=CONCATENATE(D8,"" - "",D9,"" | "",D13)
	=D12		=D10
	=D14		=D12
	=D10		=D14
	Click to see full safety and prescribing information, including boxed warning. More info		Click to see full safety and prescribing information, including boxed warning. More info
			
	Format 13		Format 14
	=CONCATENATE(D8,"" - "",D9,"" | "",D15)		=CONCATENATE(D8,"" | "",D13)
	=D10		=CONCATENATE(D9,"" "",D10)
	=D12		=D12
	=D14		=D14
	Click to see full safety and prescribing information, including boxed warning. More info		Click to see full safety and prescribing information, including boxed warning. More info
			
	Format 15		Format 16
	=CONCATENATE(D8,"" |"",D15)		=CONCATENATE(D8,"" | "",D13)
	=CONCATENATE(D9,"" "",D10)		=D12
	=D12		=D14
	=D14		=CONCATENATE(D9,"" "",D10)
	Click to see full safety and prescribing information, including boxed warning. More info		Click to see full safety and prescribing information, including boxed warning. More info
			
	Format 17		Format 18
	=CONCATENATE(D8,"" | "",D15)		=D8
	=D12		=CONCATENATE(D12,""     "",D9,"" "",D10)
	=D14		Click to see full safety and prescribing information, including boxed warning. More info
	=CONCATENATE(D9,"" "",D10)		
	Click to see full safety and prescribing information, including boxed warning. More info		
			
	Format 19		Format 20
	=D8		=CONCATENATE(D8,""     "",D12)
	=CONCATENATE(D14,""     "",D9,"" "",D10)		=CONCATENATE(D9,"" "",D10)
	Click to see full safety and prescribing information, including boxed warning. More info		Click to see full safety and prescribing information, including boxed warning. More info
			
			
	Format 21		Format 22
	=CONCATENATE(D8,""     "",D14)		=CONCATENATE(D8,"" | "",D13,""      "",D12)
	=CONCATENATE(D9,"" "",D10)		=CONCATENATE(D9,"" "",D10)
	Click to see full safety and prescribing information, including boxed warning. More info		Click to see full safety and prescribing information, including boxed warning. More info
			
			
	Format 23		Format 24
	=CONCATENATE(D8,"" | "",D13,""     "",D14)		=CONCATENATE(D15,"" - "",D8)
	=CONCATENATE(D9,"" "",D10)		=D14
	Click to see full safety and prescribing information, including boxed warning. More info		=CONCATENATE(D9,"" "",D10)
			Click to see full safety and prescribing information, including boxed warning. More info"
                .Replace("D8", "$titleCell")
                .Replace("D9", "$line1Cell")
                .Replace("D10", "$line2Cell")
                .Replace("D11", "$isiCell")
                .Replace("D12", "$displayUrl1Cell")
                .Replace("D13", "$shortenedDisplayUrl1Cell")
                .Replace("D14", "$displayUrl2Cell")
                .Replace("D15", "$shortenedDisplayUrl2Cell");

        #endregion
    }

}
